
local runtime = require 'jass.runtime'
local handle_manager = handle_manager_class:create()
local CoordinateGrouping = require 'util.坐标密度分组'
local groupByHierarchical = CoordinateGrouping.groupByHierarchical
local getGroupsCenterPoints = CoordinateGrouping.getGroupsCenterPoints

function sc.groupByHierarchical(coordinates, area)
    return groupByHierarchical(coordinates, area)
end

function sc.getGroupsCenterPoints(groups)
    return getGroupsCenterPoints(groups)
end

local sustain = {
    __tostring = function(self)
        return ('持续选取器[%s]'):format(self.handle)
    end,
}
setmetatable(sustain, sustain)

sustain.list = {}
sustain.round_area = 1024   --区域范围(半径)
sustain.interval = 50       --检查间隔不要低于30

function sustain.update(frame)
    local coordinates = {}
    for id,info in pairs(sustain.list) do
        info:update_data()
        if info.removed==nil then
            info.frame = info.frame + frame
            if info.frame>=info.timeout then
                info.frame = info.frame - frame
                info.round = info.round + 1
                coordinates[#coordinates + 1] = info
            end
        end
    end

    if #coordinates==0 then
        return
    end
    -- 使用距离分组
    local groups = CoordinateGrouping.groupByHierarchical(coordinates, sustain.round_area)

    -- 获取每组的中心点
    local centerPoints = CoordinateGrouping.getGroupsCenterPoints(groups)

    for i,group in ipairs(groups) do
        local area = 0
        local center = centerPoints[i]
        local point = ac.point(center.x,center.y)
        for _,info in ipairs(group) do
            area = math.max(area,info.area)
        end
        local units = ac.selector('unit'):range(point,sustain.round_area + area):get()
        
        for _,info in ipairs(group) do
            info:notify('select',units)
        end
    end
end


ac.loop(sustain.interval,function()
    sustain.update(sustain.interval)
end)


--结构
local mt = {}
sustain.__index = mt

mt.frame = 0
mt.timeout = 30
mt.round = 0    --运行轮次
mt.time = 10    --最大时间
mt.area = 50

--- [执行事件]
function mt:notify(name,...)
    local func = self[name]
    if func then 
        return select(2, xpcall(func, runtime.error_handle, self, ...))
    end 
end 

--[更新数据]
function mt:update_data()
    local obj = self.obj
    --对应移除自身也跟这移除
    if obj.removed then
        self:remove()
        return
    end
    local point = obj:get_point()
    local x,y = point.x,point.y
    self.x = x
    self.y = y 
end


--[筛选符合范围的单位]
function mt:select(units)
    local pos = ac.point(self.x,self.y)
    local has = {}
    for _,unit in ipairs(units) do
        local point = unit:get_point()
        if self._lock[unit.handle]==nil and pos * point<=self.area then
            self._lock[unit.handle] = true
            has[#has + 1] = unit
        end
    end
    for _,unit in ipairs(has) do
        self:notify('on_run',unit)
    end
end


--[设置持续时间]
function mt:set_time(time)
    if self.dur_timer then
        self.dur_timer:remove()
    end
    self.dur_timer = ac.wait(time*1000,function()
        self:remove()
    end)
    return self
end

function mt:set_lock(lock)
    self._lock = lock
    return self
end

--[添加]
function mt:add()
    local handle = handle_manager:allocate()
    self._handle = handle 
    self._lock = {}
    sustain.list[handle] = self

    if self.time then
        self:set_time(self.time)
    end
end

--[移除]
function mt:remove()
    if self.removed then 
        return 
    end
    self.removed = true
    if self.dur_timer then
        self.dur_timer:remove()
    end
    local handle = self._handle
    handle_manager:free(handle)
    sustain.list[handle] = nil
end


function sc.loopUnit(time,obj,area,action)
    local data = {}
    setmetatable(data, sustain)
    data.timeout = time * 1000
    data.obj = obj
    data.area = area
    data.on_run = action
    data:add()
    return data
end




--死掉单位尸体放在选取不到的位置
local death_point = ac.point(3000,3000)
ac.game:event '单位-死亡'(function(_,unit)
    ac.wait(2*1000,function()
        if unit:is_alive()==false then
            unit:set_point(death_point)
        end
    end)
end)